/*
 *
 *  Copyright (C) 2012-2024
 *  All rights reserved, Designed By 杭州连线客信息技术有限公司
 *  注意：
 *  本软件为杭州连线客信息技术有限公司开发研制，未经购买不得使用
 *  购买后可获得全部源代码（禁止转卖、分享、上传到码云、github等开源平台）
 *  一经发现盗用、分享等行为，将追究法律责任，后果自负
 *
 */
package com.lxk.pay.wx;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.RefundModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.lxk.pay.AbstractPayService;
import com.lxk.pay.model.*;
import com.lxk.pay.wx.contants.WxConstants;
import com.lxk.service.pay.constants.PayServiceBeanConstants;
import com.lxk.service.pay.param.PayRequestParam;
import com.lxk.service.setting.api.SettingServiceApi;
import com.lxk.service.setting.response.ChannelWXPayDto;
import jakarta.annotation.PostConstruct;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.ValidationUtils;
import org.springframework.scheduling.SchedulingTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author huahua
 * 2024/8/22
 */
@Slf4j
@RequiredArgsConstructor
@Service(PayServiceBeanConstants.WX_PAY_SERVICE)
public class WxPayService extends AbstractPayService {

    private final SettingServiceApi settingServiceApi;
    private final SchedulingTaskExecutor schedulingTaskExecutor;
    public static Map<String, ChannelWXPayDto> wxMap = new ConcurrentHashMap<String, ChannelWXPayDto>();
    private static final Map<String, InputStream> certFileMap = new ConcurrentHashMap<String, InputStream>();

    @PostConstruct
    @Override
    public void init() {
        schedulingTaskExecutor.execute(() -> {
            List<ChannelWXPayDto> paymsWxPayConfigVoList = settingServiceApi.wxPayAllList();
            log.info("微信支付配置装配");
            paymsWxPayConfigVoList.forEach(payProperties -> {
                if (StrUtil.isBlank(payProperties.getAppId())) {
                    return;
                }
                WxPayApiConfig wxConfig = WxPayApiConfig.builder()
                    .slAppId(payProperties.getSlAppId().getValue())
                    .slMchId(payProperties.getSlMchId().getValue())
                    .appId(payProperties.getAppId())
                    .certPath(payProperties.getCertPath().getValue())
                    .certP12Path(payProperties.getCertP12Path().getValue())
                    .keyPath(payProperties.getKeyPath().getValue())
                    .partnerKey(payProperties.getPartnerKey().getValue())
                    .apiKey(payProperties.getApiKey().getValue())
                    .apiKey3(payProperties.getApiKey3().getValue())
                    .mchId(payProperties.getMchId().getValue())
                    .domain(payProperties.getDomain().getValue())
                    .build();
                WxPayApiConfigKit.putApiConfig(wxConfig);

//            try {
//                InputStream is = certFile(payProperties.getCertP12Path().getValue());
//                if (certFileMap.get(payProperties.getAppId()) != null) {
//                    certFileMap.remove(payProperties.getAppId());
//                    certFileMap.put(payProperties.getAppId(), is);
//                } else {
//                    certFileMap.put(payProperties.getAppId(), is);
//                }
//            } catch (Exception e) {
//                log.info(e.getMessage());
//            }
                wxMap.put(payProperties.getAppId(), payProperties);
            });
            log.info("微信支付配置装配");
        });

    }

//    public InputStream certFile(String certFilePath) throws Exception {
//        File file = new File(certFilePath);
//        InputStream certStream = new FileInputStream(file);
//        return certStream;
//    }

    @Override
    public PayResultModel pay(PayRequestModel payRequestModel) {
        ValidationUtils.validate(payRequestModel, PayRequestParam.Wx.class);
        PayResultModel payResultModel = new PayResultModel();
        try {
            String appId = payRequestModel.getAppId();
            WxPayApiConfig wxPayApiConfig = null;
            if (StringUtil.isNotEmpty(appId)) {
                wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
            } else {
                wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
            }
            ChannelWXPayDto wxPayProperties = wxMap.get(wxPayApiConfig.getAppId());
            // 置换成分为单位
            BigDecimal totalFee = payRequestModel.getTotalFee().multiply(new BigDecimal("100"));
            String fee = String.valueOf(totalFee.longValue());
            String outTradeNo = StringUtil.isBlank(payRequestModel.getOutTradeNo()) ? WxPayKit.generateStr() : payRequestModel.getOutTradeNo();
            Map<String, String> params = null;
            // H5支付
            if (StrUtil.equalsIgnoreCase(TradeType.MWEB.getTradeType(), payRequestModel.getTradeTypeForThird())) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(outTradeNo)
                    .body(payRequestModel.getBody())
                    .out_trade_no(outTradeNo)
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(wxPayProperties.getWxPayNotifyUrl().getValue())
                    .trade_type(payRequestModel.getTradeTypeForThird())
                    .openid(payRequestModel.getWxOpenId())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);
                String xmlResult = WxPayApi.pushOrder(false, params);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
                log.info("微信支付结果:{}", xmlResult);
                String returnCode = result.get(WxConstants.RETURN_CODE);
                String returnMsg = result.get(WxConstants.RETURN_MSG);
                if (!WxPayKit.codeIsOk(returnCode)) {
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(returnMsg);
                    return payResultModel;
                }
                String resultCode = result.get(WxConstants.RESULT_CODE);
                if (!WxPayKit.codeIsOk(resultCode)) {
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(returnMsg);
                    return payResultModel;
                }
                // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
                String prepayId = result.get(WxConstants.PREPAY_ID);
                String webUrl = result.get(WxConstants.MWEB_URL);
                log.info("prepay_id:" + prepayId + " mweb_url:" + webUrl);
                payResultModel.getPackageParams().put(WxConstants.PREPAY_ID, prepayId);
                payResultModel.getPackageParams().put(WxConstants.MWEB_URL, webUrl);
                payResultModel.setNonceStr(result.get(WxConstants.NONCE_STR));
            }
            //小程序支付
            else if (StrUtil.equalsIgnoreCase(TradeType.JSAPI.getTradeType(), payRequestModel.getTradeTypeForThird())) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payRequestModel.getBody())
//                    .attach("连线客支付")
                    .out_trade_no(outTradeNo)
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(wxPayProperties.getWxPayNotifyUrl().getValue())
                    .trade_type(TradeType.JSAPI.getTradeType())
                    .openid(payRequestModel.getWxOpenId())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);
                String xmlResult = WxPayApi.pushOrder(false, params);
                log.info("小程序支付参数：{},结果:{}", params, xmlResult);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
                String returnCode = result.get(WxConstants.RETURN_CODE);
                String returnMsg = result.get(WxConstants.RETURN_MSG);
                if (!WxPayKit.codeIsOk(returnCode)) {
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(returnMsg);
                    return payResultModel;
                }
                String resultCode = result.get(WxConstants.RESULT_CODE);
                if (!WxPayKit.codeIsOk(resultCode)) {
                    String errCodeDes = result.get(WxConstants.ERR_CODE_DES);
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(errCodeDes);
                    return payResultModel;
                }
                // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
                String prepayId = result.get(WxConstants.PREPAY_ID);
                Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                    wxPayApiConfig.getApiKey(), SignType.MD5);
                log.info("小程序支付的参数:{}", packageParams);
                payResultModel.setPackageParams(packageParams);
                payResultModel.setNonceStr(result.get(WxConstants.NONCE_STR));
            }
            // APP支付
            else if (StrUtil.equalsIgnoreCase(TradeType.APP.getTradeType(), payRequestModel.getTradeTypeForThird())) {
                params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payRequestModel.getBody())
                    .attach(payRequestModel.getAttach())
                    .out_trade_no(outTradeNo)
                    .total_fee(fee)
                    .spbill_create_ip(payRequestModel.getUserIp())
                    .notify_url(wxPayProperties.getWxPayNotifyUrl().getValue())
                    .trade_type(TradeType.APP.getTradeType())
                    .build()
                    .createSign(wxPayApiConfig.getApiKey(), SignType.MD5);

                String xmlResult = WxPayApi.pushOrder(false, params);

                log.info("APP支付结果:{}", xmlResult);
                Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

                String returnCode = result.get(WxConstants.RETURN_CODE);
                String returnMsg = result.get(WxConstants.RETURN_MSG);
                if (!WxPayKit.codeIsOk(returnCode)) {
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(returnMsg);
                    return payResultModel;
                }
                String resultCode = result.get(WxConstants.RESULT_CODE);
                if (!WxPayKit.codeIsOk(resultCode)) {
                    payResultModel.setSuccess(false);
                    payResultModel.setMsg(returnMsg);
                    return payResultModel;
                }
                // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
                String prepayId = result.get(WxConstants.PREPAY_ID);
                Map<String, String> packageParams = WxPayKit.appPrepayIdCreateSign(wxPayApiConfig.getAppId(), wxPayApiConfig.getMchId(), prepayId,
                    wxPayApiConfig.getApiKey(), SignType.MD5);
                String jsonStr = JSON.toJSONString(packageParams);
                log.info("返回apk的参数:" + jsonStr);
                payResultModel.setPackageParams(packageParams);
                payResultModel.setNonceStr(result.get(WxConstants.NONCE_STR));
            }

        } catch (Exception e) {
            log.error("微信支付错误", e);
            payResultModel.setSuccess(false);
            payResultModel.setMsg(e.getMessage());
        }
        return payResultModel;
    }

    @Override
    public PayNotifyResultModel payNotify(Map<String, String> params, String appId) {
        PayNotifyResultModel payNotifyResultModel = new PayNotifyResultModel();
        log.info("支付通知={}", params);
        String returnCode = params.get(WxConstants.RETURN_CODE);
        WxPayApiConfig wxPayApiConfig = null;
        if (StringUtil.isNotEmpty(appId)) {
            wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
        } else {
            wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        }
        // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
        // 注意此处签名方式需与统一下单的签名类型一致
        if (WxPayKit.verifyNotify(params, wxPayApiConfig.getApiKey(), SignType.MD5)) {
            if (WxPayKit.codeIsOk(returnCode)) {
                // 更新订单信息
                // 发送通知等
                Map<String, String> xml = new HashMap<String, String>(2);
                xml.put(WxConstants.RETURN_CODE, WxConstants.SUCCESS);
                xml.put(WxConstants.RETURN_MSG, WxConstants.OK);
                payNotifyResultModel.setNotifyResult(WxPayKit.toXml(xml));
            } else {
                payNotifyResultModel.setMsg("回调状态异常" + returnCode);
                payNotifyResultModel.setSuccess(false);
            }
        } else {
            payNotifyResultModel.setMsg("回调签名错误");
            payNotifyResultModel.setSuccess(false);
        }
        return payNotifyResultModel;
    }

    /**
     * 申请退款
     *
     * @param payRequestModel
     * @return
     */
    @Override
    public RefundResultModel refund(PayRefundModel payRequestModel) {
        RefundResultModel resultModel = new RefundResultModel();
        BigDecimal totalFee = payRequestModel.getTotalFee();
        if (totalFee == null) {
            resultModel.setSuccess(false);
            resultModel.setMsg("支付金额不能为空");
            return resultModel;
        }
        if (payRequestModel.getRefundFee() == null) {
            resultModel.setSuccess(false);
            resultModel.setMsg("退款金额不能为空");
            return resultModel;
        }
        if (StrUtil.isBlank(payRequestModel.getOutTradeNo())) {
            resultModel.setSuccess(false);
            resultModel.setMsg("商户的订单号不能为空");
            return resultModel;
        }
        String appId = payRequestModel.getAppId();
        WxPayApiConfig wxPayApiConfig = null;
        if (StringUtil.isNotEmpty(appId)) {
            wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
        } else {
            wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        }

        // 置换成分为单位
//        BigDecimal totalFee = payRequestModel.getTotalFee().multiply(new BigDecimal("100"));
//        String fee = String.valueOf(totalFee.longValue());

        // 置换成分为单位
        BigDecimal refundFee = payRequestModel.getRefundFee().multiply(new BigDecimal("100"));
        totalFee = payRequestModel.getTotalFee().multiply(new BigDecimal("100"));


        ChannelWXPayDto wxPayProperties = wxMap.get(wxPayApiConfig.getAppId());

        Map<String, String> params = RefundModel.builder()
            .appid(wxPayApiConfig.getAppId())
            .mch_id(wxPayApiConfig.getMchId())
            .nonce_str(WxPayKit.generateStr())
            .refund_desc(payRequestModel.getRefundReason())
            .out_trade_no(payRequestModel.getOutTradeNo())
            .out_refund_no(StrUtil.isBlank(payRequestModel.getOutRefundNo()) ? WxPayKit.generateStr() : payRequestModel.getOutRefundNo())
            .total_fee(String.valueOf(totalFee.intValue()))
            .refund_fee(String.valueOf(refundFee.intValue()))
            .notify_url(wxPayProperties.getWxRefundNotifyUrl().getValue())
            .build()
            .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
        log.info("微信退款请求参数: {}", JSONUtil.toJsonStr(params));
        String refundStr = WxPayApi.orderRefundByProtocol(false, params, wxPayApiConfig.getCertP12Path(), wxPayApiConfig.getMchId(), "");
        log.info("微信退款返回值: {}", refundStr);
        Map<String, String> refundMap = WxPayKit.xmlToMap(refundStr);
        String returnCode = refundMap.get(WxConstants.RETURN_CODE);
        String resultMessage = refundMap.get(WxConstants.RETURN_MSG);
        String resultCode = refundMap.get(WxConstants.RESULT_CODE);
        if (!WxPayKit.codeIsOk(returnCode) || !WxPayKit.codeIsOk(resultCode)) {
            resultModel.setSuccess(false);
            resultModel.setPackageParams(refundMap);
            resultModel.setMsg(resultMessage);
        } else {
            resultModel.setSuccess(true);
            resultModel.setPackageParams(refundMap);
        }
        return resultModel;
    }

    /**
     * 订单支付状态查询
     *
     * @param requestModel
     * @return
     */
    @Override
    public OrderQueryResultModel orderQuery(OrderQueryRequestModel requestModel) {
        OrderQueryResultModel resultModel = new OrderQueryResultModel();
        String appId = requestModel.getAppId();
        WxPayApiConfig wxPayApiConfig = null;
        if (StringUtil.isNotEmpty(appId)) {
            wxPayApiConfig = WxPayApiConfigKit.getApiConfig(appId);
        } else {
            wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        }
        Map<String, String> params = RefundModel.builder()
            .appid(wxPayApiConfig.getAppId())
            .mch_id(wxPayApiConfig.getMchId())
            .nonce_str(WxPayKit.generateStr())
            .out_trade_no(requestModel.getOutTradeNo())
            .build()
            .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
        String orderQueryStr = WxPayApi.orderQuery(false, params);
        log.info("orderQueryStr: {}", orderQueryStr);
        Map<String, String> resultMap = WxPayKit.xmlToMap(orderQueryStr);
        String returnCode = resultMap.get(WxConstants.RETURN_CODE);
        String resultCode = resultMap.get(WxConstants.RESULT_CODE);
        String tradeState = resultMap.get(WxConstants.TRADE_STATE);
        String tradeStateDesc = resultMap.get(WxConstants.TRADE_STATE_DESC);
        String errCodeDes = resultMap.get(WxConstants.ERR_CODE_DES);
        if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode) && WxPayKit.codeIsOk(tradeState)) {
            resultModel.setSuccess(true);
            resultModel.setMsg(tradeState);
        } else {
            resultModel.setSuccess(false);
            if (StrUtil.isNotBlank(tradeStateDesc)) {
                resultModel.setMsg(tradeStateDesc);
            } else if (StrUtil.isNotBlank(errCodeDes)) {
                resultModel.setMsg(errCodeDes);
            } else {
                resultModel.setMsg(resultMap.get(WxConstants.RETURN_MSG));
            }
        }
        return resultModel;
    }
}
